package stream;

import exception.UknownProtocolCodeException;
import java.util.Random;
import protocol.ProtocolCode;

/**
 *
 * @author Simão Paulo Rato Alves Reis
 */
public class ByteCoding {

    public static byte[] concat(byte[]... streams) {
        byte[] stream;
        int length = 0;
        for (byte[] ba : streams) {
            length += ba.length;
        }
        stream = new byte[length];
        length = 0;
        for (byte[] ba : streams) {
            System.arraycopy(ba, 0, stream, length, ba.length);
            length += ba.length;
        }
        return stream;
    }

    public static byte[] padding(int length) {
        byte[] stream = new byte[length];
        new Random().nextBytes(stream);
        return stream;
    }

    public static byte[] get(long data) {
        byte[] stream = new byte[8];
        for (int index = 0; index < 8; index++) {
            stream[index] = (byte) ((data >>> (index << 3)) & 0xFF);
        }
        return stream;
    }

    public static long setAsLong(int pos, byte[] stream) {
        long data = 0;
        for (int index = 0; index < 8; index++) {
            data += ((long) stream[pos + index] & 0xFFL) << (index << 3);
        }
        return data;
    }

    public static byte[] get(int data) {
        return concat(new byte[]{
            (byte) (data >> 24),
            (byte) (data >> 16),
            (byte) (data >> 8),
            (byte) data},padding(4));
    }

    public static int setAsInt(int pos, byte[] stream) {
        int data = 0;
        for (int index = 0; index < 4; index++) {
            data = (int)((data << 8) | stream[index + pos]);
        }
        return data;
    }

    public static byte[] get(short data) {
        byte[] stream = new byte[2];
        for (int index = 0; index < 2; index++) {
            stream[index] = (byte) ((data >>> (index << 3)) & 0xFF);
        }
        return concat(padding(6), stream);
    }

    public static short setAsShort(int pos, byte[] stream) {
        short data = 0;
        for (int index = 6; index < 8; index++) {
            data += stream[pos + index] << ((index - 6) << 3);
        }
        return data;
    }

    public static byte[] get(String data) {
        byte[] stream = data.getBytes();
        return concat(ByteCoding.get(stream.length), stream);
    }

    public static String setAsString(int pos, byte[] stream) {
        byte[] stringstream = new byte[setAsInt(pos, stream)];
        System.arraycopy(stream, pos + 8, stringstream, 0, setAsInt(pos, stream));
        return new String(stringstream);
    }

    public static byte[] get(boolean data) {
        if (data) {
            return concat(padding(7), new byte[]{(byte) 0xFF});
        }
        return concat(padding(7), new byte[]{(byte) 0});
    }

    public static boolean setAsBoolean(int pos, byte[] stream) {
        if (stream[pos + 7] == (byte) 0) {
            return false;
        }
        return true;
    }

    public static ProtocolCode setAsProtocolCode(byte[] stream) throws UknownProtocolCodeException {
        for (ProtocolCode pc : ProtocolCode.values()) {
            if (pc.getByte() == stream[0]) {
                return pc;
            }
        }
        throw new UknownProtocolCodeException();
    }
}
